package com.zhiwei.codesecurity.service.checkmarx;

import com.zhiwei.codesecurity.common.constants.ResourcesPropertiesConstants;
import com.zhiwei.codesecurity.common.enums.InterfaceType;
import com.zhiwei.codesecurity.common.enums.RemoteType;
import com.zhiwei.codesecurity.common.enums.UploadType;
import com.zhiwei.codesecurity.common.utils.*;
import com.zhiwei.codesecurity.common.webservice.checkmarx.wsdk.args.ZipArgs;
import com.zhiwei.codesecurity.common.webservice.checkmarx.wsdk.client.SDKClient;
import com.zhiwei.codesecurity.common.webservice.checkmarx.wsdk.client.ZipPortalClient;
import com.zhiwei.codesecurity.common.webservice.checkmarx.wsdk.client.ZipSDKClient;
import com.zhiwei.codesecurity.common.webservice.checkmarx.wsdk.common.ReportStatus;
import com.zhiwei.codesecurity.common.webservice.checkmarx.wsdk.common.ReportType;
import com.zhiwei.codesecurity.common.webservice.checkmarx.wsdk.common.ScanData;
import com.zhiwei.codesecurity.common.webservice.checkmarx.wsdk.common.TaskResult;
import com.zhiwei.codesecurity.common.enums.TaskStatus;
import com.zhiwei.codesecurity.entity.Interfaces;
import com.zhiwei.codesecurity.entity.Projects;
import com.zhiwei.codesecurity.entity.ScheduleJob;
import com.zhiwei.codesecurity.entity.TaskscanResults;
import com.zhiwei.codesecurity.mapper.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.util.Date;
import java.util.List;

import static com.zhiwei.codesecurity.common.constants.ResourcesPropertiesConstants.MAX_QUEUE_COUNT;

@Service("checkMarxService")
public class CheckMarxServiceImpl implements ICheckMarxService {
    private final static Logger logger = LoggerFactory.getLogger(CheckMarxServiceImpl.class);

    private final static String SDK_WS_URL = "/cxwebinterface/sdk/cxsdkwebservice.asmx?wsdl";
    private final static String PORTAL_WS_URL = "/cxwebinterface/portal/cxwebservice.asmx?wsdl";

    @Autowired
    private ProjectsDao projectsDao;

    @Autowired
    private ScheduleJobDao scheduleJobDao;

    @Autowired
    private InterfacesDao interfacesDao;

    @Autowired
    private TaskscanResultsDao taskscanResultsDao;

    @Autowired
    private TaskscanResultsMapper taskscanResultsMapper;

    /**
     * 提交扫描任务
     * @param projectId
     * 项目ID
     * @param interfaceId
     * 接口工具ID
     * @throws FileNotFoundException
     * @throws IOException
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
    public void submitScanTask(long projectId, String interfaceId, long scheduleJobId) {
        logger.info("==============Begin 执行提交扫描任务|projectId: {}, interfaceId: {}", projectId, interfaceId);
        Projects projects = projectsDao.getByPrimaryKey(projectId);
        logger.info(">>>>>>>>>>>>>项目名称:{}，项目ID:{}", projects.getProjectName(), projectId);
        if (null == projects) {
            logger.error("============项目信息不存在!|projectId:{}",projectId);
            return;
        } else if (projects.getUploadType().intValue() == UploadType.REMOTE_UPLOAD.getKey().intValue()) { // 上传类型为远程上传
            String fileName = UUIDUtil.getUUID();
            String srcDirectory = ResourcesPropertiesConstants.TEMP_DIR + fileName;
            String zipFileAddress = "/";
            if (projects.getRemoteType().intValue() == RemoteType.SVN.getKey().intValue()) { // SVN 获取扫描代码
                zipFileAddress = ResourcesPropertiesConstants.SVN_DOWNLOAD_DIR + fileName + ".zip";
                SVNUtils svnUtils = new SVNUtils(projects.getSvnGitUsername(), projects.getSvnGitPwd());
                File workingCopy = new File(srcDirectory);
                if (workingCopy.exists()) {
                    workingCopy.delete();
                }
                workingCopy.mkdirs();
                svnUtils.checkout(projects.getSvnGitUrl(), workingCopy, true);
                FileUtils.FileToZip2(srcDirectory, zipFileAddress);
            } else if (projects.getRemoteType().intValue() == RemoteType.GIT.getKey().intValue()) { // GIT 获取扫描代码
                zipFileAddress = ResourcesPropertiesConstants.GIT_DOWNLOAD_DIR + fileName + ".zip";
                JGitUtils.cloneRepository(projects.getSvnGitUrl(), projects.getSvnGitUsername(),
                        projects.getSvnGitPwd(), projects.getGitBranchname(), srcDirectory);
                logger.info(">>>>>>>>>>>>压缩前的源目录:{}，压缩后的ZIP文件目录:{}", srcDirectory, zipFileAddress);
                FileUtils.FileToZip2(srcDirectory, zipFileAddress);
            }
            File zipFile = new File(zipFileAddress);
            if (!zipFile.exists()) { // 退出本次扫描任务
                return;
            }
            projects.setFileName(zipFileAddress);
            projects.setUpdateTime(new Date());
            projects.setDataVersion(projects.getDataVersion() + 1);
            projectsDao.update(projects);
        }
        logger.info("===========项目信息查询成功|projects: {}", projects);
        Interfaces interfaces = interfacesDao.getByPrimaryKey(interfaceId);
        if (null == interfaces) {
            logger.error("============接口工具信息不存在!|interfaces: {}", interfaces);
            return;
        }
        logger.info("===========接口工具信息查询成功|interfaces: {}", interfaces);
        TaskscanResults taskscanResults = new TaskscanResults();
        taskscanResults.setScheduleJobId(scheduleJobId);
        taskscanResults.setInterfaceId(Long.parseLong(interfaceId));
        taskscanResults.setInterfaceType(InterfaceType.CHECKMARX.getKey());
        taskscanResults.setReportType(ReportType.PDF.getKey());
        taskscanResults.setTaskStatus(TaskStatus.UNSUBMIT.getKey());
        logger.info("===========准备添加扫描任务结果记录|taskscanResults: {}", taskscanResults);
        if (0 != taskscanResultsDao.add(taskscanResults)) {
            logger.info("===========添加扫描任务结果记录成功!==============");
        } else {
            logger.info("===========添加扫描任务结果记录失败!==============");
        }
        logger.info("==============End 执行提交扫描任务|projectId: {}, interfaceId: {}, filePath: {}", projectId, interfaceId);
    }

    /**
     * 定时任务：获取表t_taskscan_results中task_status状态为0（未提交）的任务，
     * 当CheckMarx引擎中队列数没有超出最大可排队数时，提交任务。
     */
    public synchronized void submitScanTaskToCK() throws FileNotFoundException, IOException {
        logger.info("==============Begin CheckMarx 提交扫描任务到CheckMarx=============");
        List<TaskscanResults> resultsList = taskscanResultsMapper.queryUnsubmitScanTask(InterfaceType.CHECKMARX.getKey());
        if (null != resultsList && resultsList.size() > 0) {
            Interfaces interfaces = new Interfaces();
            interfaces.setInterfaceType(InterfaceType.CHECKMARX.getKey());
            interfaces = interfacesDao.query(interfaces);
            if (null == interfaces) {
                logger.error("==============系统不存在对应的接口工具信息!|InterfaceType:{}", InterfaceType.CHECKMARX.getValue());
                return;
            }

            ZipArgs zipArgs = new ZipArgs();
            zipArgs.setSdkUrl(interfaces.getUrlAddress() + PORTAL_WS_URL);
            zipArgs.setSdkUsr(interfaces.getAccount());
            zipArgs.setSdkPwd(interfaces.getPassword());
            logger.info("=============构造ZipPortalArgs参数完成======================");
            int queueCount = 0;
            try {
                ZipPortalClient zipPortalClient = new ZipPortalClient(zipArgs); // 主要为了获取队列排队数量
                queueCount = ZipPortalClient.getQueueCount(zipPortalClient);
                zipPortalClient.logout();
            } catch (Exception ex) {
                logger.error(">>>>>>>>>>>CheckMarx 提交扫描任务到CheckMarx执行失败:{}", ex.getMessage());
            }
            if (queueCount >= MAX_QUEUE_COUNT) {
                logger.info("===========当前CheckMarx队列排队数量已经达到最大值，请稍后再试!");
                return;
            }
            int remainderQueueCount = MAX_QUEUE_COUNT - queueCount;
            int processingTaskCount = Math.min(remainderQueueCount, resultsList.size());

            TaskscanResults results;
            for (int i = 0; i < processingTaskCount; i++) {
                results = resultsList.get(i);
                long scheduleJobId = results.getScheduleJobId();
                ScheduleJob scheduleJobCon = new ScheduleJob();
                scheduleJobCon.setScheduleJobId(scheduleJobId);
                ScheduleJob scheduleJob = scheduleJobDao.query(scheduleJobCon);
                long projectId = scheduleJob.getProjectId();
                Projects projects = projectsDao.getByPrimaryKey(projectId);

                zipArgs.setSdkUrl(interfaces.getUrlAddress() + SDK_WS_URL);
                zipArgs.setPreset(interfaces.getCkPreset());
                zipArgs.setGroup(interfaces.getCkGroup());
                zipArgs.setConfig(interfaces.getCkConfig());
                zipArgs.setProjectName(projects.getProjectName());
                zipArgs.setOwner("admin");
                zipArgs.setDescription("description");
                zipArgs.setZipName(projects.getFileName());
                File file = new File(projects.getFileName());
                FileInputStream inputStream = new FileInputStream(file);
                byte[] bytes = new byte[inputStream.available()];
                inputStream.read(bytes);
                inputStream.close();
                zipArgs.setZipBytes(bytes);
                logger.info("=============构造ZipSDKArgs参数完成======================");
                try {
                    ZipSDKClient zipSDKClient = new ZipSDKClient(zipArgs);

                    TaskResult taskResult = zipSDKClient.submitScanTask();
                    if (null != taskResult) {
                        results.setProjectId(taskResult.getProjectId());
                        results.setRunId(String.valueOf(taskResult.getSubmitRunId()));
                        results.setTaskStatus(TaskStatus.WAITING_TO_PROCESS.getKey());
                        results.setDataVersion(results.getDataVersion() + 1);
                        if (0 != taskscanResultsDao.update(results)) {
                            logger.info("===========更新扫描任务结果记录成功!==============");
                        } else {
                            logger.info("===========更新扫描任务结果记录失败!==============");
                        }
                    }
                    zipSDKClient.logout();
                    logger.info("===========提交扫描任务到CheckMarx成功!|taskResult: {}", taskResult);
                }  catch (Exception ex){
                    logger.error(">>>>>>>>>>>CheckMarx 提交扫描任务到CheckMarx执行失败:{}", ex.getMessage());
                }
            }
        }
        logger.info("==============End CheckMarx 提交扫描任务到CheckMarx=============");
    }

    /**
     * 定时更新扫描任务的相关执行状态；（根据CheckMarx服务返回的状态进行更新）
     */
    public void updateScanTaskStatus()  {
        logger.info("==============Begin CheckMarx更新扫描任务的相关执行状态=============");
        List<TaskscanResults> resultsList = taskscanResultsMapper.queryUncompletedScanTask(InterfaceType.CHECKMARX.getKey());
        if (null != resultsList && resultsList.size() > 0) {
            Interfaces interfaces = new Interfaces();
            interfaces.setInterfaceType(InterfaceType.CHECKMARX.getKey());
            interfaces = interfacesDao.query(interfaces);
            if (null == interfaces) {
                logger.error("==============系统不存在对应的接口工具信息!|InterfaceType:{}", InterfaceType.CHECKMARX.getValue());
                return;
            }

            ZipArgs zipArgs = new ZipArgs();
            zipArgs.setSdkUrl(interfaces.getUrlAddress() + SDK_WS_URL);
            zipArgs.setSdkUsr(interfaces.getAccount());
            zipArgs.setSdkPwd(interfaces.getPassword());
            logger.info("=============构造ZipSDKArgs参数完成======================");

            try {
                ZipSDKClient zipSDKClient = new ZipSDKClient(zipArgs);
                TaskscanResults results = null;
                for (int i = 0; i < resultsList.size(); i++) {
                    results = resultsList.get(i);
                    logger.info("============开始查询扫描任务结果记录|TaskscanResults:{}", results);
                    TaskResult taskResult = SDKClient.getScanStatus(zipSDKClient, results.getRunId());
                    logger.info("============查询扫描任务结果:{}", taskResult);
                    if (null == taskResult) {
                        continue;
                    }

                    results.setProjectId(taskResult.getProjectId());
                    results.setTaskId(taskResult.getTaskId());
                    results.setScanId(taskResult.getScanId());
                    results.setTaskStatus(taskResult.getCurrentStatus().getKey());
                    results.setUpdateTime(new Date());
                    results.setDataVersion(results.getDataVersion() + 1);
                    if (0 != taskscanResultsDao.update(results)) {
                        logger.info("===========更新扫描任务结果记录成功|results:{}", results);
                    } else {
                        logger.error("===========更新扫描任务结果记录失败|results:{}", results);
                    }
                }
                zipSDKClient.logout();
            } catch (Exception ex) {
                logger.error(">>>>>>>>>>>CheckMarx更新扫描任务的相关执行状态执行失败:{}", ex.getMessage());
            }
        }
        logger.info("==============End CheckMarx更新扫描任务的相关执行状态=============");
    }

    /**
     * 提交生成扫描报告任务
     */
    public void createScanTaskReport() {
        logger.info("==============Begin CheckMarx提交生成扫描报告任务=============");
        TaskscanResults taskscanResultsCon = new TaskscanResults();
        taskscanResultsCon.setTaskStatus(TaskStatus.FINISHED.getKey());
        taskscanResultsCon.setReportId(0L);
        List<TaskscanResults> resultsList = taskscanResultsDao.list(taskscanResultsCon);
        if (null != resultsList && resultsList.size() > 0) {
            Interfaces interfaces = new Interfaces();
            interfaces.setInterfaceType(InterfaceType.CHECKMARX.getKey());
            interfaces = interfacesDao.query(interfaces);
            if (null == interfaces) {
                logger.error("==============系统不存在对应的接口工具信息!|InterfaceType:{}", InterfaceType.CHECKMARX.getValue());
                return;
            }

            ZipArgs zipArgs = new ZipArgs();
            zipArgs.setSdkUrl(interfaces.getUrlAddress() + SDK_WS_URL);
            zipArgs.setSdkUsr(interfaces.getAccount());
            zipArgs.setSdkPwd(interfaces.getPassword());
            logger.info("=============构造ZipSDKArgs参数完成======================");

            try {
                ZipSDKClient zipSDKClient = new ZipSDKClient(zipArgs);

                // 获取所有扫描项目的扫描结果信息
                List<ScanData> scanDataArray = SDKClient.getScanDisplayData(zipSDKClient);

                TaskscanResults results;
                for (int i = 0; i < resultsList.size(); i++) {
                    results = resultsList.get(i);
                    logger.info("============开始获取ScanID:{}的扫描数据========", results.getScanId());
                    if (null != scanDataArray && 0 != scanDataArray.size()) {
                        for (ScanData scanData : scanDataArray) {
                            if (scanData.getScanID().longValue() == results.getScanId().longValue()) {
                                results.setScanDisplayData(scanData.toString());
                                break;
                            }
                        }
                    }

                    logger.info("============开始生成扫描报告|TaskscanResults:{}", results);
                    ReportType reportType = ReportType.getInstance(results.getReportType());
                    long reportId = SDKClient.createScanReport(zipSDKClient, results.getScanId(), reportType);
                    logger.info("============生成扫描报告提交成功|reportId: {}", reportId);
                    results.setReportId(reportId);
                    results.setReportStatus(ReportStatus.PROCESSING.getKey());
                    results.setUpdateTime(new Date());
                    results.setDataVersion(results.getDataVersion() + 1);
                    if (0 != taskscanResultsDao.update(results)) {
                        logger.info("===========更新扫描任务结果记录成功|results:{}", results);
                    } else {
                        logger.error("===========更新扫描任务结果记录失败|results:{}", results);
                    }
                }
                zipSDKClient.logout();
            } catch (Exception ex) {
                logger.error(">>>>>>>>>>>CheckMarx提交生成扫描报告任务执行失败:{}", ex.getMessage());
            }
        }
        logger.info("==============end CheckMarx提交生成扫描报告任务=============");
    }

    /**
     * 获取扫描报告
     * @param filePath
     * 扫描报告保存路径
     * @throws Exception
     */
    public void getScanTaskReport(String filePath) {
        logger.info("==============begin CheckMarx获取生成扫描报告=============");
        TaskscanResults taskscanResultsCon = new TaskscanResults();
        taskscanResultsCon.setReportStatus(ReportStatus.PROCESSING.getKey());
        List<TaskscanResults> resultsList = taskscanResultsDao.list(taskscanResultsCon);
        if (null != resultsList && resultsList.size() > 0) {
            Interfaces interfaces = new Interfaces();
            interfaces.setInterfaceType(InterfaceType.CHECKMARX.getKey());
            interfaces = interfacesDao.query(interfaces);
            if (null == interfaces) {
                logger.error("==============系统不存在对应的接口工具信息!|InterfaceType:{}", InterfaceType.CHECKMARX.getValue());
                return;
            }

            ZipArgs zipArgs = new ZipArgs();
            zipArgs.setSdkUrl(interfaces.getUrlAddress() + SDK_WS_URL);
            zipArgs.setSdkUsr(interfaces.getAccount());
            zipArgs.setSdkPwd(interfaces.getPassword());
            logger.info("=============构造ZipSDKArgs参数完成======================");
            try {
                ZipSDKClient zipSDKClient = new ZipSDKClient(zipArgs);
                TaskscanResults results;
                for (int i = 0; i < resultsList.size(); i++) {
                    results = resultsList.get(i);
                    logger.info("============开始获取生成扫描报告|TaskscanResults:{}", results);
                    long reportId = results.getReportId();
                    ReportStatus reportStatus = SDKClient.getReportStatus(zipSDKClient, reportId);
                    // 如果当前报告状态为：处理中，不获取扫描报告，处理下一条
                    if (ReportStatus.PROCESSING == reportStatus) {
                        continue;
                    }

                    if (ReportStatus.COMPLETED == reportStatus) {
                        byte[] reportBytes = SDKClient.getReport(zipSDKClient, reportId);
                        ReportType reportType = ReportType.getInstance(results.getReportType());
                        String fileName = UUIDUtil.getUUID() + "." + reportType.getValue();
                        FileUtils.saveFile(filePath.concat(fileName), reportBytes);
                        results.setReportName(fileName);
                        logger.info("============获取扫描报告成功|ReportName: {}", results.getReportName());
                    }
                    results.setReportStatus(reportStatus.getKey());
                    results.setUpdateTime(new Date());
                    results.setDataVersion(results.getDataVersion() + 1);
                    taskscanResultsDao.update(results);
                }
                zipSDKClient.logout();
            } catch (Exception ex) {
                logger.error(">>>>>>>>>>>CheckMarx获取生成扫描报告执行失败:{}", ex.getMessage());
            }
        }
        logger.info("==============end CheckMarx获取生成扫描报告=============");
    }

    /**
     * 取消当前扫描任务
     * @param interfaceId
     * @param runId
     */
    public void cancelScan(Long interfaceId, String runId) {
        logger.info("==============begin CheckMarx取消当前扫描任务=============");
        Interfaces interfaces = interfacesDao.getByPrimaryKey(interfaceId);
        if (null == interfaces) {
            logger.error("==============系统不存在对应的接口工具信息!|interfaceId:{}", interfaceId);
            return;
        }
        logger.info("=============begin: 构造ZipSDKArgs参数======================");
        ZipArgs zipArgs = new ZipArgs();
        zipArgs.setSdkUrl(interfaces.getUrlAddress() + SDK_WS_URL);
        zipArgs.setSdkUsr(interfaces.getAccount());
        zipArgs.setSdkPwd(interfaces.getPassword());
        logger.info("=============end: 构造ZipSDKArgs参数完成======================");
        try {
            ZipSDKClient zipSDKClient = new ZipSDKClient(zipArgs);
            SDKClient.cancelScan(zipSDKClient, runId);
            zipSDKClient.logout();
        } catch (Exception ex) {
            logger.error(">>>>>>>>>>>CheckMarx取消当前扫描任务执行失败:{}", ex.getMessage());
        }
        logger.info("==============end CheckMarx取消当前扫描任务=============");
    }
}
